<?php

namespace slinks\extensions\action\configuration;

use slinks\core\HttpFoundation\IRequest;
use slinks\extensions\action\result\IResult;
use slinks\core\DependencyInjection\ContainerInterface;
use slinks\extensions\action\routing\RouterInterface;
use slinks\extensions\action\core\IActionContext;
use slinks\extensions\action\core\ActionInvocation;
use slinks\extensions\action\core\ContainerActionProxy;
use slinks\extensions\action\routing\Matcher\Exception\NotFoundException;

class Configuration implements IConfiguration {
    /* @var $container ContainerInterface */

    private $container;

    function __construct(ContainerInterface $container) {
        $this->container = $container;
    }

    /**
     * Find the Action Mapping for a given request
     *
     * @param string $actionMappingName the name of the IActionMapping.
     * @return IActionMapping the action mapping for this request.
     */
    public function findActionMapping($actionMappingName) {
        $mapping = null;
        // if no mapping parameter then no mappings have been defined.
        if($this->container->hasParameter(ConfigurationConstants::actionMappingsParameter())){
            $mapping = $this->getArrayParameterValue(ConfigurationConstants::actionMappingsParameter(), $actionMappingName);
        }
        if ($mapping == null) {
            throw new NotFoundException('Cannot find action mapping for ' . $actionMappingName);
        }
        return $mapping;
    }
    
    /**
     * @return array of all configured IActionMappings keyed by the IActionMapping->getName().
     */
    public function getAllActionMappings(){
        $ret = array();
        if($this->container->hasParameter(ConfigurationConstants::actionMappingsParameter())){
            $ret = $this->container->getParameter(ConfigurationConstants::actionMappingsParameter());
        }
        return $ret;
    }

    /**
     * Find a IActionInvocation for the Action mapping.
     *
     * @param IActionContext $actionContext for the current request.
     * @return IActionInvocation
     */
    public function findActionInvocation(IActionContext $actionContext) {
        return new ActionInvocation(new ContainerActionProxy($this->container, $actionContext),
                                                             $this->findInterceptors($actionContext->getActionMapping()),
                                                             $actionContext);
    }

    /**
     * Find the IResult implementation for the given IResultMapping.
     *
     * @param IResultMapping $resultMapping
     */
    public function findResult(IResultMapping $resultMapping) {
        $resultName = $resultMapping->getType();
        if ($resultName == null) {
            $resultName = $this->getDefaultResultName();
        }
        $resultConfig = $this->getArrayParameterValue(ConfigurationConstants::resultConfigsParameter(), $resultName);

        if (!isset($resultConfig)) {
            throw new \Exception('No Result found for ' . $resultName);
        }

        return $this->container->get($resultConfig->getClassName());
    }

    private function findInterceptors(IActionMapping $actionMapping) {
        $ref = $actionMapping->getInterceptorStackName();
        if ($ref == null) {
            $ref = $this->getDefaultInterceptorStackName();
        }

        $stack = $this->getArrayParameterValue(ConfigurationConstants::interceptorStacksParameter(), $ref);

        if (!isset($stack)) {
            throw new \Exception('InterceptorStack not configured ' . $ref);
        }
        return $stack;
    }

    private function getArrayParameterValue($parameterName, $key) {
        $arrayParameter = $this->container->getParameter($parameterName);
        return (isset($arrayParameter[$key]) ? $arrayParameter[$key] : null);
    }

    /**
     *
     * @return
     */
    public function getDefaultInterceptorStackName() {
        return $this->container->getParameter(ConfigurationConstants::defaultInterceptorStackParameter());
    }

    /**
     *
     * @return
     */
    public function getDefaultResultName() {
        return $this->container->getParameter(ConfigurationConstants::defaultResultParameter());
    }

}